﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Drawing;
using System.Threading;
using System.Windows.Interop;
using System.EnterpriseServices;
using System.Windows;
using System.Windows.Threading;
using System.Collections;

// Ooganizer Namespaces
using Ooganizer.Platform;
using Ooganizer.Platform.Win32;
using Ooganizer.Resolver;
using Ooganizer.Server;
using Ooganizer.API;

namespace Ooganizer.Client.UI
{
   
    /// <summary>
    /// Use this to send down to Buttonhook so that it knows what to show
    /// </summary>
    public enum BUTTON_HOOK_STATE
    {
        BUTTON_NONE = 0,
        BUTTON_ADD = 1,
        BUTTON_DELETE = 2
    }
    /// <summary>
    /// This class manages the creation/deletion and actions of (1 - n) ButtonForms
    /// </summary>
    [ComVisible(false)]
    internal class ButtonFormMgr
    {
        private Hashtable m_ObjectList = null;
        private const int INITIAL_OBJECT_CAPACITY = 30;
        private Dispatcher m_Dispatcher = null;

        // we have one btnForm loaded (hidden) always, as a performance cache
        private ButtonForm m_btnForm = null;
        private bool m_IsBtnFormCached = false;

        // Imp! - allows external caller to run any action on a ButtonForm
        public delegate void _Action(ButtonForm btnForm);

        //custom private delegates
        private delegate void delegate_Create(int hParentWND, int hButtonWND, BUTTON_HOOK_STATE buttonstate);
        private delegate void delegate_Delete(int hParentWND);
        private delegate int delegate_CreateWindow(int hParentWND);
        private delegate void delegate_Action(int hParentWND, _Action action);

        public ButtonFormMgr(Dispatcher disp)
        {
            m_ObjectList = new Hashtable();
            m_Dispatcher = disp;

            if (!m_IsBtnFormCached)
                BetterPerformance();
        }
        /// <summary>
        /// Calls Create_ButtonForm via Dispatcher if neccessary
        /// </summary>        
        /// <param name="hParentWND">handle to Parent/Owner Window</param>
        public void Create_ButtonFormDISP(int hParentWND, int hButtonWND, BUTTON_HOOK_STATE buttonState)
        {
            if (m_Dispatcher.Thread == Thread.CurrentThread)
            {
                Create_ButtonForm(hParentWND, hButtonWND, buttonState);
            }
            else
            {
                object[] parameters = new object[] { hParentWND , hButtonWND, buttonState};
                m_Dispatcher.Invoke((delegate_Create)Create_ButtonFormDISP, System.Windows.Threading.DispatcherPriority.Normal, parameters);
            }
        }
        /// <summary>
        /// Calls Create_ButtonFormWindow via Dispatcher if neccessary
        /// </summary>        
        /// <param name="hParentWND">handle to Parent/Owner Window</param>
        public int Create_ButtonFormWindowDISP(int hParentWND)
        {
            if (m_Dispatcher.Thread == Thread.CurrentThread)
            {
                return Create_ButtonFormWindow(hParentWND);
            }
            else
            {
                object[] parameters = new object[] { hParentWND };
                return (int)m_Dispatcher.Invoke((delegate_CreateWindow)Create_ButtonFormWindowDISP, System.Windows.Threading.DispatcherPriority.Normal, parameters);
            }
        }
        /// <summary>
        /// Calls Delete_ButtonForm via Dispatcher if neccessary
        /// </summary>        
        /// <param name="hParentWND">handle to Parent/Owner Window</param>
        public void Delete_ButtonFormDISP(int hParentWND)
        {
            if (m_Dispatcher.Thread == Thread.CurrentThread)
            {
                Delete_ButtonForm(hParentWND);
            }
            else
            {
                object[] parameters = new object[] { hParentWND };
                m_Dispatcher.Invoke((delegate_Delete)Delete_ButtonFormDISP, System.Windows.Threading.DispatcherPriority.Normal, parameters);
            }
            return;
        }
        /// <summary>
        /// Use this Dispatching Action Function to run any _Action on the ButtonForm
        /// </summary>        
        /// <param name="hParentWND">handle to Parent/Owner Window</param>
        public void RunAction_ButtonFormDISP(int hParentWND, _Action action)
        {
            if (m_Dispatcher.Thread == Thread.CurrentThread)
            {                                
                RunAction_ButtonForm(hParentWND, action);
            }
            else
            {
                object[] parameters = new object[] { hParentWND, action };
                m_Dispatcher.Invoke((delegate_Action)RunAction_ButtonFormDISP, System.Windows.Threading.DispatcherPriority.Normal, parameters);
            }
        }        
        /// <summary>
        /// Calls Terminate via Dispatcher if neccessary
        /// </summary>
        /// <param name="hParentWND">handle to Parent/Owner Window</param>
        public void TerminateDISP()
        {
            if (m_Dispatcher.Thread == Thread.CurrentThread)
            {
                Terminate();
            }
            else
            {
                m_Dispatcher.Invoke((Action)TerminateDISP, System.Windows.Threading.DispatcherPriority.Normal, null);
            }
            return;
        }
        /// <summary>
        /// Creates a new ButtonForm object into the ObjectList
        /// </summary>
        /// <param name="hParentWND">handle to Parent/Owner Window</param>
        private void Create_ButtonForm(int hParentWND, int hButtonWND, BUTTON_HOOK_STATE buttonState)
        {
            try
            {
                if (!m_IsBtnFormCached)
                    BetterPerformance();

                m_ObjectList[hParentWND] = new ButtonForm();
                ButtonForm btnForm = (ButtonForm)m_ObjectList[hParentWND];

                if (btnForm != null)
                {
                    // We use the InteropHelper to set the Owner Property
                    WindowInteropHelper InteropHelper = new WindowInteropHelper(btnForm);
                    InteropHelper.Owner = (IntPtr)hParentWND;

                    // Set the important Fields into the Button Form
                    btnForm.HookedWindow = (IntPtr)hParentWND; // give it the handle to the hooked window
                    btnForm.ButtonWindow = (IntPtr)hButtonWND; // give it the handle to the button window
                    btnForm.ButtonState = buttonState;         // give it the state the button is in

                    RECT ParentWndRect = new RECT();
                    Win32Functions.GetWindowRect((IntPtr)hParentWND, out ParentWndRect);

                    // Get Initial Location for the form
                    btnForm.Top = ParentWndRect.top;
                    btnForm.Left = ParentWndRect.left;

                    // Get Initial Height and Width of the form
                    btnForm.Height = (ParentWndRect.bottom - ParentWndRect.top);
                    btnForm.Width = (ParentWndRect.right - ParentWndRect.left);
                }                
            }
            catch (Exception e)
            {
                Log.Error(string.Format("{0}() - error thrown",
                          System.Reflection.MethodBase.GetCurrentMethod().Name), e);
            }
        }
        /// <summary>
        /// Use this function to delete the ButtonForm Object
        /// </summary>
        /// <param name="hParentWND">handle to Parent/Owner Window</param>
        private void Delete_ButtonForm(int hParentWND)
        {
            try
            {
                if (m_ObjectList.ContainsKey(hParentWND))
                {
                    ButtonForm btnForm = (ButtonForm)m_ObjectList[hParentWND];
                    if (btnForm != null)
                    {
                        m_ObjectList.Remove(hParentWND);
                        btnForm.Close();                        
                    }
                }
            }
            catch (Exception e)
            {
                Log.Error(string.Format("{0}() - error thrown",
                          System.Reflection.MethodBase.GetCurrentMethod().Name), e);
            }
        }
        /// <summary>
        /// Use this to run void() function actions on the specified buttonform
        /// </summary>
        /// <param name="hParentWND">handle to Parent/Owner Window</param>
        /// <param name="action">a pointer to a delegate (action function) to run</param>
        private void RunAction_ButtonForm(int hParentWND, _Action action)
        {
            try
            {
                if (m_ObjectList.ContainsKey(hParentWND))
                {
                    ButtonForm btnForm = (ButtonForm)m_ObjectList[hParentWND];
                    if (btnForm != null)
                        action(btnForm);
                }
            }
            catch (Exception e)
            {
                Log.Error(string.Format("{0}() - error thrown",
                          System.Reflection.MethodBase.GetCurrentMethod().Name), e);
            }
        }
        /// <summary>
        /// Use this to actually create the Window, call this after calling Create_ButtonForm
        /// This will create the window by calling Show(), this will also show the window
        /// </summary>
        /// <param name="hParentWND">handle to Parent/Owner Window</param>
        /// <returns>the Handle to the newly created window object</returns>
        private int Create_ButtonFormWindow(int hParentWND)
        {
            try
            {
                if (m_ObjectList.ContainsKey(hParentWND))
                {
                    ButtonForm btnForm = (ButtonForm)m_ObjectList[hParentWND];

                    // We use the InteropHelper to see if this WPFForm has been created previously
                    WindowInteropHelper InteropHelper = new WindowInteropHelper(btnForm);
                    if (InteropHelper.Handle == IntPtr.Zero)
                    {
                        btnForm.Show();                   
                    }
                    return (int)InteropHelper.Handle;
                }
            }
            catch (Exception e)
            {
                Log.Error(string.Format("{0}() - error thrown",
                          System.Reflection.MethodBase.GetCurrentMethod().Name), e);
            }
            return 0;
        }
        /// <summary>
        /// Kills all ButtonForm Instances (STOPS ALL)
        /// </summary>
        private void Terminate()
        {
            try
            {
                foreach (object o in m_ObjectList)
                {
                    ButtonForm btnForm = (ButtonForm)o;
                    btnForm.Close();                    
                }
                m_ObjectList.Clear();

                if (m_IsBtnFormCached)
                {
                    m_btnForm.Close();
                    m_IsBtnFormCached = false;
                }
            }
            catch (Exception e)
            {
                Log.Error(string.Format("{0}() - error thrown",
                          System.Reflection.MethodBase.GetCurrentMethod().Name), e);
            }
        }
        /// <summary>
        /// In order to improve first WPForm performance we have one
        /// WPForm ButtonForm loaded at all times. (opacity set to 0).
        /// ~it has no parent setting so it is just a form of the desktop
        /// </summary>
        private void BetterPerformance()
        {
            if (!m_IsBtnFormCached)
            {
                try
                {
                    // Performance Cache (keeps a window loaded always)
                    m_btnForm = new ButtonForm();
                    m_btnForm.Opacity = 0;
                    m_btnForm.ShowInTaskbar = false;
                    m_btnForm.Show();
                    m_btnForm.Hide();
                    m_IsBtnFormCached = true;
                }
                catch (Exception e)
                {
                    Log.Error(string.Format("{0}() - error thrown",
                              System.Reflection.MethodBase.GetCurrentMethod().Name), e);
                }
            }
        }

        #region log4net declaration

        private log4net.ILog Log
        {
            get
            {
                if (_logWrapper == null)
                {
                    _logWrapper = new LogServerClientWrapper(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
                }

                return _logWrapper.Log;
            }
        }

        private LogServerClientWrapper _logWrapper;

        #endregion
    }
    /// <summary>
    /// ButtonWPFormCCW - Com Callable Wrapper Class exposed to ButtonHook.
    /// This class is responsible for creating the ButtonWPForm (a WPF Button)
    /// </summary>
    [Guid("6F108F0B-9CEB-4d3d-B1D9-7685F9F39E50")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("Ooganizer.ButtonWPFormCCW")]
    [ComVisible(true)]
    [ObjectPooling(Enabled = true, MinPoolSize = 25, MaxPoolSize = 250, CreationTimeout = 5000)]
    public class ButtonWPFormCCW : ServicedComponent, IButtonForm, ITerminatable
    {
        private const int WM_W32SETBUTTONSTATE = (Platform.Win32.Win32_Constants.WM_USER + 701);

        /// <summary>
        /// WPF Application Object and Thread
        /// </summary>        
        private static ButtonFormMgr s_ButtonFormMgr = null;
        private static Thread s_thread = null;
        private static bool s_bApplicationIsRunning = false;

        /// Member Variables
        private IntPtr m_hWnd = IntPtr.Zero;        // handle to the WPF Window we create here in this class
        private IntPtr m_hWndButton = IntPtr.Zero;  // handle to the Win32 Button passed into us by Acticate
        private IntPtr m_hWndParent = IntPtr.Zero;  // handle to the Window we are hooked into passed into us by Activate
        private const bool m_bStartWPFormHidden = true;
        BUTTON_HOOK_STATE m_ButtonState = BUTTON_HOOK_STATE.BUTTON_NONE;

        /// <summary>
        /// Message Loop Thread for ButtonWPForm - handled internally
        /// </summary>        
        private static void ThreadProc()
        {                        
            if (s_ButtonFormMgr == null)
            {
                s_ButtonFormMgr = new ButtonFormMgr(System.Windows.Threading.Dispatcher.CurrentDispatcher);                
                s_bApplicationIsRunning = true;                
                //Enters Message Loop
                System.Windows.Threading.Dispatcher.Run();
            }
        }
        /// <summary>
        /// Use this function to start ThreadProc(above) if needed. COM+ can shutdown the thread anytime,
        /// we need to make sure that the thread is alive BEFORE calling ButtonForms
        /// </summary>
        private void StartThreadIfNeeded()
        {
            if (s_thread != null && s_thread.IsAlive)
            {
                return;
            }
            else
            {
                s_bApplicationIsRunning = false;

                // Start a new Thread so it can become the Message Loop
                s_thread = new Thread(new ThreadStart(ButtonWPFormCCW.ThreadProc));

                // GUI components require the thread to be STA; otherwise throws an error
                s_thread.SetApartmentState(ApartmentState.STA);
                s_thread.Start();

                // Make sure the Application Object is running 
                // (COM will eventually just time out otherwise)
                while (!s_bApplicationIsRunning)
                    System.Threading.Thread.Sleep(20); // Syncronous call
            }
        }
        public ButtonWPFormCCW()
        {
            Instantiate();                        
        }
        ~ButtonWPFormCCW()
        {         
        }
        /// <summary>
        /// ITerminatable - Use it to instantiate the ButtonFormCCW
        /// </summary>
        public void Instantiate()
        {
            StartThreadIfNeeded();            
        }
        /// <summary>
        /// ITerminatable - Terminates all ButtonForm Objects and shutsdown the application thread
        /// (only call when you want to do a kill all)
        /// </summary>
        public void Terminate()
        {
            if (s_bApplicationIsRunning)
            {
                s_ButtonFormMgr.TerminateDISP();
                s_ButtonFormMgr = null;
                s_thread.Abort();                
                s_bApplicationIsRunning = false;
            }
        }
        /// <summary>
        /// This is called first time when the buttonhook is attaching into a Window. It passes us 
        /// important handle information that we need to communicate back state information.
        /// Additionally, we set the intial state of the button right here.
        /// </summary>
        /// <param name="hParentWND">the handle to the window the buttonhook is hooked into</param>
        /// <param name="hButtonWND">the handle to the button Window (we need it if we want to communicat with it)</param>
        public void WPForm_Activated(int hParentWND,int hButtonWND)
        {
            try
            {
                // Store the important handle information for later use if needed
                m_hWndParent = (IntPtr)hParentWND;
                m_hWndButton = (IntPtr)hButtonWND;

                // We need to resolve the window here
                ResolverDispatch resolver = new Resolver.ResolverDispatch();
                ArtifactGroup artifacts = resolver.GetArtifacts((IntPtr)hParentWND);

                // If we can resolve this window then we want to be checking
                if (artifacts.Length >= 1)
                {
                    Artifact curArtifact = artifacts.GetPrimary;
                    bool bFound = false;

                    using (OoganizerState oogyState = new OoganizerState())
                    {
                        string curWorkspaceName = oogyState.GetCurrentWorkspace();

                        if (curWorkspaceName != null)
                        {
                            using (OoganizerAPIProxy proxy = oogyState.GetAPIProxy())
                            {
                                Workspace curWorkspace = proxy.API.GetWorkspace(curWorkspaceName);

                                foreach (OoganizerDAL.Artifact artifact in curWorkspace.Artifacts)
                                {
                                    if (curArtifact.Location.ToUpper() == artifact.Location.ToUpper())
                                    {
                                        bFound = true;
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    if (bFound)
                        SetW32ButtonToNewState(BUTTON_HOOK_STATE.BUTTON_DELETE);
                    else
                        SetW32ButtonToNewState(BUTTON_HOOK_STATE.BUTTON_ADD);
                }
                else
                {
                    Log.Error(string.Format("{0}() - WPForm could not resolve this Window - disabling the Caption Button",
                              System.Reflection.MethodBase.GetCurrentMethod().Name));

                    SetW32ButtonToNewState(BUTTON_HOOK_STATE.BUTTON_NONE);
                }
            }
            catch (Exception e)
            {
                Log.Error(string.Format("{0}() - WPForm could not resolve this Window - disabling the Caption Button",
                          System.Reflection.MethodBase.GetCurrentMethod().Name), e);
            }
        }
        /// <summary>
        /// Use this to Both set a new ButtonState Internally as well as communicate the
        /// state change down to the W32 button to display it.
        /// </summary>
        /// <param name="buttonState"></param>
        internal void SetW32ButtonToNewState(BUTTON_HOOK_STATE buttonState)
        {
            if (m_hWndButton != IntPtr.Zero)
            {
                m_ButtonState = buttonState;
                Platform.Win32.Win32Functions.PostMessage((IntPtr)m_hWndButton, WM_W32SETBUTTONSTATE, IntPtr.Zero, (IntPtr)m_ButtonState);
            }
        }
        /// <summary>
        /// This function is called when the Win32 button is clicked. This function should
        /// handle the creation of the wpfform and display it over the window
        /// </summary>        
        /// <returns>the window handle to the newly created wpfform to be stored by the caller</returns>
        public int Open()
        {            
            try
            {
                // Create the WpfForm object and then create the wpfform window and show it. 
                s_ButtonFormMgr.Create_ButtonFormDISP((int)m_hWndParent, (int)m_hWndButton, m_ButtonState);
                m_hWnd = (IntPtr)s_ButtonFormMgr.Create_ButtonFormWindowDISP((int)m_hWndParent);
                return (int) m_hWnd;
            }
            catch (Exception e) 
            {
                Log.Error(string.Format("{0}() - error thrown",
                          System.Reflection.MethodBase.GetCurrentMethod().Name), e);
            }
            return 0;
        }
        /// <summary>       
        /// Closes the wpfform associated with this COM instance.
        /// </summary>        
        public void Close()
        {            
            try
            {
                if(m_hWndParent != IntPtr.Zero)
                    s_ButtonFormMgr.Delete_ButtonFormDISP((int) m_hWndParent);
            }
            catch (Exception e)
            {
                Log.Error(string.Format("{0}() - error thrown",
                          System.Reflection.MethodBase.GetCurrentMethod().Name), e);
            }
        }               

        /// <summary>
        /// Maximize Event occured - make sure to set the size
        /// </summary>        
        //public void Event_MaximizeOccured(int nHeight, int nWidth)
        //{
        //    try
        //    {
        //        ButtonFormMgr._Action action = new ButtonFormMgr._Action(delegate(ButtonForm btnForm)
        //        {                                        
        //            btnForm.Height = nHeight;
        //            btnForm.Width = nWidth;                 
        //        });
        //        s_ButtonFormMgr.RunAction_ButtonFormDISP((int)m_hWndParent, action);
        //    }
        //    catch (Exception e)
        //    {
        //        Logger.Log("Event_MaximizeOccured an error " + e.Message);
        //    }
        //}            

        #region log4net declaration

        private log4net.ILog Log
        {
            get
            {
                if (_logWrapper == null)
                {
                    _logWrapper = new LogServerClientWrapper(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
                }

                return _logWrapper.Log;
            }
        }

        private LogServerClientWrapper _logWrapper;

        #endregion
    }
}
